/*
Copyright 2022 The Crossplane Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

syntax = "proto3";

import "google/protobuf/struct.proto";
import "google/protobuf/duration.proto";

package apiextensions.fn.proto.v1beta1;

option go_package = "github.com/crossplane/crossplane/apis/apiextensions/fn/proto/v1beta1";

// A FunctionRunnerService is a Composition Function.
service FunctionRunnerService {
  // RunFunction runs the Composition Function.
  rpc RunFunction(RunFunctionRequest) returns (RunFunctionResponse) {}
}

// A RunFunctionRequest requests that the Composition Function be run.
message RunFunctionRequest {
  // Metadata pertaining to this request.
  RequestMeta meta = 1;

  // The observed state prior to invocation of a Function pipeline. State passed
  // to each Function is fresh as of the time the pipeline was invoked, not as
  // of the time each Function was invoked.
  State observed = 2;

  // Desired state according to a Function pipeline. The state passed to a
  // particular Function may have been accumulated by previous Functions in the
  // pipeline.
  //
  // Note that the desired state must be a partial object with only the fields
  // that this function (and its predecessors in the pipeline) wants to have
  // set in the object. Copying a non-partial observed state to desired is most
  // likely not what you want to do. Leaving out fields that had been returned
  // as desired before will result in them being deleted from the objects in the
  // cluster.
  State desired = 3;

  // Optional input specific to this Function invocation. A JSON representation
  // of the 'input' block of the relevant entry in a Composition's pipeline.
  optional google.protobuf.Struct input = 4;

  // Optional context. Crossplane may pass arbitary contextual information to a
  // Function. A Function may also return context in its RunFunctionResponse,
  // and that context will be passed to subsequent Functions. Crossplane
  // discards all context returned by the last Function in the pipeline.
  optional google.protobuf.Struct context = 5;

  // Optional extra resources that the Function required.
  // Note that extra resources is a map to Resources, plural.
  // The map key corresponds to the key in a RunFunctionResponse's
  // extra_resources field. If a Function requested extra resources that
  // did not exist, Crossplane sets the map key to an empty Resources message to
  // indicate that it attempted to satisfy the request.
  map<string, Resources> extra_resources = 6;
}

// Resources represents the state of several Crossplane resources.
message Resources {
  repeated Resource items = 1;
}

// A RunFunctionResponse contains the result of a Composition Function run.
message RunFunctionResponse {
  // Metadata pertaining to this response.
  ResponseMeta meta = 1;

  // Desired state according to a Function pipeline. Functions may add desired
  // state, and may mutate or delete any part of the desired state they are
  // concerned with. A Function must pass through any part of the desired state
  // that it is not concerned with.
  //
  //
  // Note that the desired state must be a partial object with only the fields
  // that this function (and its predecessors in the pipeline) wants to have
  // set in the object. Copying a non-partial observed state to desired is most
  // likely not what you want to do. Leaving out fields that had been returned
  // as desired before will result in them being deleted from the objects in the
  // cluster.
  State desired = 2;

  // Results of the Function run. Results are used for observability purposes.
  repeated Result results = 3;

  // Optional context to be passed to the next Function in the pipeline as part
  // of the RunFunctionRequest. Dropped on the last function in the pipeline.
  optional google.protobuf.Struct context = 4;

  // Requirements that must be satisfied for this Function to run successfully.
  Requirements requirements = 5;
}

// RequestMeta contains metadata pertaining to a RunFunctionRequest.
message RequestMeta {
  // An opaque string identifying the content of the request. Two identical
  // requests should have the same tag.
  string tag = 1;
}

// Requirements that must be satisfied for a Function to run successfully.
message Requirements {
  // Extra resources that this Function requires.
  // The map key uniquely identifies the group of resources.
  map<string, ResourceSelector> extra_resources = 1;
}

// ResourceSelector selects a group of resources, either by name or by label.
message ResourceSelector {
  string api_version = 1;
  string kind = 2;

  oneof match {
    string match_name = 3;
    MatchLabels match_labels = 4;
  }
}

// MatchLabels defines a set of labels to match resources against.
message MatchLabels {
  map<string, string> labels = 1;
}

// ResponseMeta contains metadata pertaining to a RunFunctionResponse.
message ResponseMeta {
  // An opaque string identifying the content of the request. Must match the
  // meta.tag of the corresponding RunFunctionRequest.
  string tag = 1;

  // Time-to-live of this response. Deterministic Functions with no side-effects
  // (e.g. simple templating Functions) may specify a TTL. Crossplane may choose
  // to cache responses until the TTL expires.
  optional google.protobuf.Duration ttl = 2;
}

// State of the composite resource (XR) and any composed resources.
message State {
  // The state of the composite resource (XR).
  Resource composite = 1;

  // The state of any composed resources.
  map<string, Resource> resources = 2;
}

// A Resource represents the state of a composite or composed resource.
message Resource {
  // The JSON representation of the resource.
  //
  // * Crossplane will set this field in a RunFunctionRequest to the entire
  //   observed state of a resource - including its metadata, spec, and status.
  //
  // * A Function should set this field in a RunFunctionRequest to communicate
  //   the desired state of a composite or composed resource.
  //
  // * A Function may only specify the desired status of a composite resource -
  //   not its metadata or spec. A Function should not return desired metadata
  //   or spec for a composite resource. This will be ignored.
  //
  // * A Function may not specify the desired status of a composed resource -
  //   only its metadata and spec. A Function should not return desired status
  //   for a composed resource. This will be ignored.
  google.protobuf.Struct resource = 1;

  // The resource's connection details.
  // 
  // * Crossplane will set this field in a RunFunctionRequest to communicate the
  //   the observed connection details of a composite or composed resource.
  //
  // * A Function should set this field in a RunFunctionResponse to indicate the
  //   desired connection details of the composite resource.
  //
  // * A Function should not set this field in a RunFunctionResponse to indicate
  //   the desired connection details of a composed resource. This will be
  //   ignored.
  map<string, bytes> connection_details = 2;

  // Ready indicates whether the resource should be considered ready.
  // 
  // * Crossplane will never set this field in a RunFunctionRequest.
  //
  // * A Function should set this field to READY_TRUE in a RunFunctionResponse
  //   to indicate that a desired composed resource is ready.
  //
  // * A Function should not set this field in a RunFunctionResponse to indicate
  //   that the desired composite resource is ready. This will be ignored.
  Ready ready = 3;
}

// Ready indicates whether a composed resource should be considered ready.
enum Ready {
  READY_UNSPECIFIED = 0;

  // True means the composed resource has been observed to be ready.
  READY_TRUE = 1;

  // False means the composed resource has not been observed to be ready.
  READY_FALSE = 2;
}

// A Result of running a Function.
message Result {
  // Severity of this result.
  Severity severity = 1;

  // Human-readable details about the result.
  string message = 2;
}

// Severity of Function results.
enum Severity {
  SEVERITY_UNSPECIFIED = 0;

  // Fatal results are fatal; subsequent Composition Functions may run, but
  // the Composition Function pipeline run will be considered a failure and
  // the first fatal result will be returned as an error.
  SEVERITY_FATAL = 1;

  // Warning results are non-fatal; the entire Composition will run to
  // completion but warning events and debug logs associated with the
  // composite resource will be emitted.
  SEVERITY_WARNING = 2;

  // Normal results are emitted as normal events and debug logs associated
  // with the composite resource.
  SEVERITY_NORMAL = 3;
}